package beanstao.util.collections;

import java.lang.reflect.Array;
import java.util.ArrayList;

import net.sf.json.JSONArray;

import beanstao.util.Util;

public class LinkedArray<T>
{

	public LinkedArray()
	{
		this(256);
	}

	public LinkedArray(int size)
	{
		this(null, size);
	}

	public LinkedArray(Class<T> eleType, int size)
	{
		this.eleType = eleType;
		if (size < 0)
		{
			Util.makeThrow("width must >0!");
		}
		this.width = size;
		this.cache = new ArrayList<T[]>();
	}

	private Class<T> eleType;
	private int offset;
	private int cursor;
	private int width;
	private ArrayList<T[]> cache;

	@SuppressWarnings("unchecked")
	public LinkedArray<T> push(T e)
	{
		T[] array;
		int row = this.cursor / this.width;
		int i = this.cursor % this.width;
		if ((this.cache.size() == 0) || ((this.cursor != this.offset) && (i == 0)))
		{
			if (null == this.eleType)
			{
				array = (T[]) Array.newInstance(e.getClass(), this.width);
			}
			else
			{
				array = (T[]) Array.newInstance(this.eleType, this.width);
			}
			this.cache.add(array);
		}
		else
		{
			array = this.cache.get(row);
		}
		array[i] = e;
		this.cursor++;
		return this;
	}

	public LinkedArray<T> pushAll(T... es)
	{
		for (T e : es)
		{
			this.push(e);
		}
		return this;
	}

	public T popFirst()
	{
		return this.innerGet(this.offset++);
	}

	public String popFirst(int num)
	{
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < num; i++)
		{
			sb.append(this.popFirst());
		}
		return sb.toString();
	}

	public T popLast()
	{
		return this.innerGet(--this.cursor);
	}

	public LinkedArray<T> popLast(int num)
	{
		for (int i = 0; i < num; i++)
		{
			this.popLast();
		}
		return this;
	}

	public T first()
	{
		if (this.size() == 0)
		{
			return null;
		}
		return this.innerGet(this.offset);
	}

	public T last()
	{
		if (this.size() == 0)
		{
			return null;
		}
		return this.innerGet(this.cursor - 1);
	}

	public LinkedArray<T> set(int index, T e)
	{
		this.checkBound(index);
		index += this.offset;
		T[] array = this.cache.get(index / this.width);
		array[index % this.width] = e;
		return this;
	}

	private void checkBound(int index)
	{
		if ((index >= this.size()) || (index < 0))
		{
			throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size());
		}
	}

	public LinkedArray<T> clear()
	{
		this.cache.clear();
		this.cursor = 0;
		this.offset = 0;
		return this;
	}

	private T innerGet(int index)
	{
		T[] array = this.cache.get(index / this.width);
		return array[index % this.width];
	}

	public T get(int index)
	{
		this.checkBound(index);
		return this.innerGet(index + this.offset);
	}

	public boolean isEmpty()
	{
		return 0 == this.cursor - this.offset;
	}

	public int size()
	{
		return this.cursor - this.offset;
	}

	@SuppressWarnings("unchecked")
	public T[] toArray()
	{
		if (this.size() == 0)
		{
			if (null == this.eleType)
			{
				return (T[]) new Object[0];
			}
			return (T[]) Array.newInstance(this.eleType, 0);
		}
		T[] re;
		if (null == this.eleType)
		{
			re = (T[]) Array.newInstance(this.first().getClass(), this.size());
		}
		else
		{
			re = (T[]) Array.newInstance(this.eleType, this.size());
		}
		for (int i = 0; i < re.length; i++)
		{
			re[i] = this.get(i);
		}
		return re;
	}

	@Override
	public String toString()
	{
		return JSONArray.fromObject(this.toArray()).toString();
	}

	public String popAll()
	{
		String re = this.toString();
		this.clear();
		return re;
	}

	public boolean contains(T obj)
	{
		for (int i = 0; i < this.size(); i++)
		{
			if (this.get(i).equals(obj))
			{
				return true;
			}
		}
		return false;
	}
}
